from __future__ import annotations

import colorsys
import os
from collections import defaultdict
from typing import List, Optional

import numpy as np
import pyqtgraph.opengl as gl
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QColor, QVector3D

from config.settings import settings


class UnionFind:
    def __init__(self) -> None:
        self.parent: dict[int, int] = {}

    def find(self, node: int) -> int:
        if node not in self.parent:
            self.parent[node] = node
        while self.parent[node] != node:
            self.parent[node] = self.parent[self.parent[node]]
            node = self.parent[node]
        return node

    def union(self, u: int, v: int) -> None:
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            self.parent[root_v] = root_u


class ComponentOperationsDialog(QtWidgets.QDialog):
    def __init__(self, parent: Optional[QtWidgets.QWidget] = None, obj_data=None, groups=None) -> None:
        super().__init__(parent)
        self.setWindowTitle("组件操作")
        self.setGeometry(200, 200, 1200, 800)

        self.obj_data = obj_data
        self.groups = groups or []
        self.color_map: List[tuple[float, float, float, float]] = []
        self.component_meshes: List[gl.GLMeshItem] = []
        self.text_items = []
        self.line_items = []
        self.current_mesh: Optional[gl.GLMeshItem] = None
        self.original_mesh: Optional[gl.GLMeshItem] = None
        self.background_color = settings.viewer.background_color
        self.wireframe_mode = False
        self.surface_mode = True

        self.generate_color_map(12)
        self.create_ui()

    # ------------------------------------------------------------------ #
    # UI helpers
    # ------------------------------------------------------------------ #
    def generate_color_map(self, num_colors: int) -> None:
        for i in range(num_colors):
            hue = i / num_colors
            saturation = 0.9
            value = 0.9
            r, g, b = colorsys.hsv_to_rgb(hue, saturation, value)
            self.color_map.append((r, g, b, 1.0))

    def create_ui(self) -> None:
        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.setContentsMargins(10, 10, 10, 10)
        main_layout.setSpacing(15)

        self.view = gl.GLViewWidget()
        self.view.setCameraPosition(distance=50)
        self.view.setBackgroundColor(self.background_color)
        axis = gl.GLAxisItem()
        axis.setSize(10, 10, 10)
        self.view.addItem(axis)
        main_layout.addWidget(self.view, 4)

        right_panel = QtWidgets.QFrame()
        right_panel.setFrameShape(QtWidgets.QFrame.StyledPanel)
        right_panel.setMinimumWidth(300)
        right_panel_layout = QtWidgets.QVBoxLayout(right_panel)
        right_panel_layout.setContentsMargins(10, 10, 10, 10)
        right_panel_layout.setSpacing(15)

        btn_group = QtWidgets.QGroupBox("操作")
        btn_group.setStyleSheet(
            "QGroupBox { border: 1px solid #505050; border-radius: 5px; margin-top: 10px; }"
            "QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px; }"
        )
        btn_group_layout = QtWidgets.QGridLayout(btn_group)
        btn_group_layout.setContentsMargins(10, 20, 10, 10)
        btn_group_layout.setSpacing(10)

        btn_load = QtWidgets.QPushButton("加载 OBJ")
        btn_load.clicked.connect(self.load_obj)
        btn_load.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogOpenButton))
        btn_group_layout.addWidget(btn_load, 0, 0)

        btn_split = QtWidgets.QPushButton("分割模型")
        btn_split.clicked.connect(self.split_model)
        btn_split.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_FileDialogDetailedView))
        btn_group_layout.addWidget(btn_split, 0, 1)

        btn_merge = QtWidgets.QPushButton("合并并保存")
        btn_merge.clicked.connect(self.merge_components)
        btn_merge.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogSaveButton))
        btn_group_layout.addWidget(btn_merge, 1, 0)

        right_panel_layout.addWidget(btn_group)

        display_group = QtWidgets.QGroupBox("显示选项")
        display_group_layout = QtWidgets.QVBoxLayout(display_group)
        display_group_layout.setContentsMargins(10, 20, 10, 10)
        display_group_layout.setSpacing(10)

        view_layout = QtWidgets.QHBoxLayout()
        btn_reset = QtWidgets.QPushButton("重置视图")
        btn_reset.clicked.connect(self.reset_view)
        view_layout.addWidget(btn_reset)

        btn_bg_color = QtWidgets.QPushButton("背景颜色")
        btn_bg_color.clicked.connect(self.set_background_color)
        view_layout.addWidget(btn_bg_color)
        display_group_layout.addLayout(view_layout)

        mode_layout = QtWidgets.QHBoxLayout()
        self.wireframe_action = QtWidgets.QPushButton("线框模式")
        self.wireframe_action.setCheckable(True)
        self.wireframe_action.toggled.connect(self.toggle_wireframe)
        mode_layout.addWidget(self.wireframe_action)

        self.surface_action = QtWidgets.QPushButton("表面模式")
        self.surface_action.setCheckable(True)
        self.surface_action.setChecked(True)
        self.surface_action.toggled.connect(self.toggle_surface)
        mode_layout.addWidget(self.surface_action)
        display_group_layout.addLayout(mode_layout)

        opacity_label = QtWidgets.QLabel("透明度:")
        display_group_layout.addWidget(opacity_label)

        self.opacity_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self.opacity_slider.setRange(0, 100)
        self.opacity_slider.setValue(100)
        self.opacity_slider.valueChanged.connect(self.update_opacity)
        display_group_layout.addWidget(self.opacity_slider)

        right_panel_layout.addWidget(display_group)

        component_group = QtWidgets.QGroupBox("组件列表")
        component_group_layout = QtWidgets.QVBoxLayout(component_group)
        component_group_layout.setContentsMargins(0, 20, 0, 0)

        self.search_box = QtWidgets.QLineEdit()
        self.search_box.setPlaceholderText("搜索组件...")
        self.search_box.textChanged.connect(self.filter_components)
        component_group_layout.addWidget(self.search_box)

        self.list_widget = QtWidgets.QListWidget()
        self.list_widget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
        self.list_widget.itemChanged.connect(self.update_display)
        component_group_layout.addWidget(self.list_widget)

        list_btn_layout = QtWidgets.QHBoxLayout()
        btn_select_all = QtWidgets.QPushButton("全选")
        btn_select_all.clicked.connect(self.select_all_components)
        list_btn_layout.addWidget(btn_select_all)

        btn_deselect_all = QtWidgets.QPushButton("全不选")
        btn_deselect_all.clicked.connect(self.deselect_all_components)
        list_btn_layout.addWidget(btn_deselect_all)
        component_group_layout.addLayout(list_btn_layout)

        right_panel_layout.addWidget(component_group)
        right_panel_layout.addStretch(1)

        btn_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
        btn_box.accepted.connect(self.accept)
        btn_box.rejected.connect(self.reject)
        right_panel_layout.addWidget(btn_box)

        main_layout.addWidget(right_panel, 1)

        if self.obj_data:
            self.plot_original()

    # ------------------------------------------------------------------ #
    # Event handlers
    # ------------------------------------------------------------------ #
    def set_background_color(self) -> None:
        color = QtWidgets.QColorDialog.getColor(initial=QColor(self.background_color), parent=self, title="选择背景颜色")
        if color.isValid():
            self.background_color = color.name()
            self.view.setBackgroundColor(self.background_color)

    def reset_view(self) -> None:
        if self.obj_data:
            self.auto_zoom()

    def update_opacity(self, value: int) -> None:
        opacity = value / 100.0
        try:
            if self.current_mesh:
                color = self.color_map[0] if hasattr(self, "color_map") else (0.8, 0.8, 0.8, opacity)
                self.current_mesh.setColor((color[0], color[1], color[2], opacity))

            for i, mesh in enumerate(self.component_meshes):
                if i < len(self.color_map):
                    color = self.color_map[i]
                    mesh.setColor((color[0], color[1], color[2], opacity))
                else:
                    mesh.setColor((0.8, 0.8, 0.8, opacity))
        except Exception as exc:  # pragma: no cover - defensive UI update
            print(f"更新透明度时出错: {exc}")

    def toggle_wireframe(self, checked: bool) -> None:
        self.wireframe_mode = checked
        self.update_display_mode()

    def toggle_surface(self, checked: bool) -> None:
        self.surface_mode = checked
        self.update_display_mode()

    def select_all_components(self) -> None:
        for i in range(self.list_widget.count()):
            item = self.list_widget.item(i)
            item.setCheckState(QtCore.Qt.Checked)

    def deselect_all_components(self) -> None:
        for i in range(self.list_widget.count()):
            item = self.list_widget.item(i)
            item.setCheckState(QtCore.Qt.Unchecked)

    def filter_components(self) -> None:
        search_text = self.search_box.text().lower()
        for i in range(self.list_widget.count()):
            item = self.list_widget.item(i)
            item_text = item.text().lower()
            item.setHidden(search_text not in item_text)

    # ------------------------------------------------------------------ #
    # Core operations
    # ------------------------------------------------------------------ #
    def load_obj(self) -> None:
        path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开 OBJ 文件", "", "OBJ 文件 (*.obj)")
        if not path:
            return

        try:
            self.obj_data = self.parse_obj(path)
            self.plot_original()
        except Exception as exc:
            QtWidgets.QMessageBox.critical(self, "错误", f"加载文件失败:\n{exc}")

    def parse_obj(self, path: str) -> dict:
        vertices = []
        faces = []

        with open(path, "r", encoding="utf-8") as f:
            for line in f:
                if line.startswith("v "):
                    parts = line.strip().split()
                    vertices.append([float(p) for p in parts[1:]])
                elif line.startswith("f "):
                    parts = line.strip().split()
                    face = []
                    for part in parts[1:]:
                        v_idx = int(part.split("/")[0]) - 1
                        face.append(v_idx)
                    faces.append(face)

        if not vertices or not faces:
            raise ValueError("无效的 OBJ 文件 - 缺少顶点或面数据")

        return {"vertices": np.array(vertices), "faces": faces, "path": path}

    def plot_original(self) -> None:
        try:
            self.view.clear()
            self.text_items = []
            self.line_items = []
            self.component_meshes = []

            if self.obj_data is None or len(self.obj_data["vertices"]) == 0:
                return

            valid_faces = []
            for face in self.obj_data["faces"]:
                if len(set(face)) == 3:
                    v1, v2, v3 = face
                    a = self.obj_data["vertices"][v1]
                    b = self.obj_data["vertices"][v2]
                    c = self.obj_data["vertices"][v3]
                    ab = b - a
                    ac = c - a
                    cross = np.cross(ab, ac)
                    area = 0.5 * np.linalg.norm(cross)

                    if area > 1e-6:
                        valid_faces.append(face)

            self.original_mesh = self.create_mesh(self.obj_data["vertices"], valid_faces, (0.8, 0.8, 0.8, 1))
            self.current_mesh = self.original_mesh
            self.view.addItem(self.original_mesh)

            self.auto_zoom()
        except Exception as exc:
            QtWidgets.QMessageBox.critical(self, "错误", f"绘制模型时出错:\n{exc}")

    def auto_zoom(self) -> None:
        if not self.obj_data:
            return

        vertices = self.obj_data["vertices"]
        if len(vertices) == 0:
            return

        min_coords = np.min(vertices, axis=0)
        max_coords = np.max(vertices, axis=0)
        size = np.max(max_coords - min_coords)
        center = (min_coords + max_coords) / 2
        distance = size * 2

        center_vec = QVector3D(float(center[0]), float(center[1]), float(center[2]))
        camera_pos = QVector3D(float(center[0]), float(center[1]), float(center[2] + distance))

        self.view.setCameraPosition(pos=camera_pos, distance=distance, elevation=30, azimuth=45)

    def split_model(self) -> None:
        if self.obj_data is None:
            QtWidgets.QMessageBox.warning(self, "警告", "请先加载 OBJ 文件")
            return

        try:
            uf = UnionFind()

            valid_faces = []
            for face in self.obj_data["faces"]:
                if len(set(face)) == 3:
                    v1, v2, v3 = face
                    a = self.obj_data["vertices"][v1]
                    b = self.obj_data["vertices"][v2]
                    c = self.obj_data["vertices"][v3]
                    ab = b - a
                    ac = c - a
                    cross = np.cross(ab, ac)
                    area = 0.5 * np.linalg.norm(cross)

                    if area > 1e-6:
                        valid_faces.append(face)
                        uf.union(v1, v2)
                        uf.union(v2, v3)
                        uf.union(v3, v1)

            groups = defaultdict(list)
            for v_idx in range(len(self.obj_data["vertices"])):
                root = uf.find(v_idx)
                groups[root].append(v_idx)

            self.groups = []
            all_vertices = self.obj_data["vertices"]
            for idx, (_, v_indices) in enumerate(groups.items()):
                component_vertices = all_vertices[v_indices]

                index_map = {v: i for i, v in enumerate(v_indices)}
                group_faces = []
                for face in valid_faces:
                    if all(v in v_indices for v in face):
                        converted_face = [index_map[v] for v in face]
                        group_faces.append(converted_face)

                self.groups.append(
                    {
                        "vertices": component_vertices,
                        "faces": group_faces,
                        "v_indices": v_indices,
                        "color": self.color_map[idx % len(self.color_map)],
                        "number": idx + 1,
                    }
                )

            self.list_widget.clear()
            for i, group in enumerate(self.groups):
                item = QtWidgets.QListWidgetItem(f"组件 {i + 1} (顶点: {len(group['vertices'])})")
                item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
                item.setCheckState(QtCore.Qt.Checked)
                self.list_widget.addItem(item)

            self.update_display()
        except Exception as exc:
            QtWidgets.QMessageBox.critical(self, "错误", f"分割模型时出错:\n{exc}")

    def create_mesh(self, vertices, faces, color):
        mesh = gl.GLMeshItem(
            vertexes=vertices,
            faces=np.array(faces),
            color=color,
            smooth=True,
            shader="shaded",
            glOptions="opaque",
        )

        if self.wireframe_mode:
            mesh.setGLOptions("additive")

        return mesh

    def update_display(self) -> None:
        self.view.clear()
        self.text_items = []
        self.line_items = []
        self.component_meshes = []

        axis = gl.GLAxisItem()
        axis.setSize(10, 10, 10)
        self.view.addItem(axis)

        for i in range(self.list_widget.count()):
            if self.list_widget.item(i).checkState() == QtCore.Qt.Checked:
                group = self.groups[i]
                mesh = self.create_mesh(group["vertices"], group["faces"], group["color"])
                self.view.addItem(mesh)
                self.component_meshes.append(mesh)

    def merge_components(self) -> None:
        selected = [i for i in range(self.list_widget.count()) if self.list_widget.item(i).checkState() == QtCore.Qt.Checked]

        if len(selected) < 1:
            QtWidgets.QMessageBox.warning(self, "警告", "请先选择要合并的组件")
            return

        default_path = ""
        if self.obj_data and "path" in self.obj_data:
            original_path = self.obj_data["path"]
            dirname = os.path.dirname(original_path)
            basename = os.path.splitext(os.path.basename(original_path))[0]
            default_path = os.path.join(dirname, f"{basename}_merged.obj")

        path, _ = QtWidgets.QFileDialog.getSaveFileName(self, "保存合并文件", default_path, "OBJ 文件 (*.obj)")
        if not path:
            return

        if not path.lower().endswith(".obj"):
            path += ".obj"

        try:
            merged_vertices = []
            merged_faces = []
            vertex_map = {}

            for idx in selected:
                group = self.groups[idx]
                for old_idx in group["v_indices"]:
                    if old_idx not in vertex_map:
                        vertex_map[old_idx] = len(merged_vertices)
                        merged_vertices.append(self.obj_data["vertices"][old_idx])

                for face in group["faces"]:
                    merged_face = [vertex_map[group["v_indices"][v]] + 1 for v in face]
                    merged_faces.append(merged_face)

            with open(path, "w", encoding="utf-8") as f:
                f.write("# 由高级 OBJ 组件分割管理器生成\n")
                f.write(f"# 原始文件: {self.obj_data.get('path', '未知')}\n")
                f.write(f"# 合并了 {len(selected)} 个组件\n\n")

                for vertex in merged_vertices:
                    f.write(f"v {vertex[0]:.6f} {vertex[1]:.6f} {vertex[2]:.6f}\n")

                f.write("\n")
                for face in merged_faces:
                    f.write(f"f {' '.join(map(str, face))}\n")

            QtWidgets.QMessageBox.information(self, "保存成功", f"合并文件已保存到：\n{path}")
        except Exception as exc:
            QtWidgets.QMessageBox.critical(self, "错误", f"保存文件时出错:\n{exc}")


class UnionFind:
    def __init__(self):
        self.parent = {}

    def find(self, u):
        if u not in self.parent:
            self.parent[u] = u
        while self.parent[u] != u:
            self.parent[u] = self.parent[self.parent[u]]
            u = self.parent[u]
        return u

    def union(self, u, v):
        root_u = self.find(u)
        root_v = self.find(v)
        if root_u != root_v:
            self.parent[root_v] = root_u


class ComponentOperationsDialog(QtWidgets.QDialog):
    def __init__(self, parent=None, obj_data=None, groups=None):
        super().__init__(parent)
        self.setWindowTitle("组件操作")
        self.setGeometry(200, 200, 1200, 800)

        # 初始化数据
        self.obj_data = obj_data
        self.groups = groups or []
        self.color_map = []
        self.text_items = []
        self.line_items = []
        self.component_meshes = []
        self.generate_color_map(12)
        self.current_mesh = None
        self.original_mesh = None
        self.background_color = "#2D2D2D"
        self.wireframe_mode = False
        self.surface_mode = True
        # 创建界面
        self.create_ui()

    def generate_color_map(self, num_colors):
        for i in range(num_colors):
            hue = i / num_colors
            saturation = 0.9
            value = 0.9
            r, g, b = colorsys.hsv_to_rgb(hue, saturation, value)
            self.color_map.append((r, g, b, 1.0))

    def create_ui(self):
        main_layout = QtWidgets.QHBoxLayout(self)
        main_layout.setContentsMargins(10, 10, 10, 10)
        main_layout.setSpacing(15)

        # 3D视图
        self.view = gl.GLViewWidget()
        self.view.setCameraPosition(distance=50)
        self.view.setBackgroundColor(self.background_color)

        # 添加坐标轴
        axis = gl.GLAxisItem()
        axis.setSize(10, 10, 10)
        self.view.addItem(axis)

        main_layout.addWidget(self.view, 4)

        # 右侧面板
        right_panel = QtWidgets.QFrame()
        right_panel.setFrameShape(QtWidgets.QFrame.StyledPanel)
        right_panel.setMinimumWidth(300)
        right_panel_layout = QtWidgets.QVBoxLayout(right_panel)
        right_panel_layout.setContentsMargins(10, 10, 10, 10)
        right_panel_layout.setSpacing(15)

        # 操作按钮组
        btn_group = QtWidgets.QGroupBox("操作")
        btn_group.setStyleSheet("QGroupBox { border: 1px solid #505050; border-radius: 5px; margin-top: 10px; }"
                                "QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px; }")
        btn_group_layout = QtWidgets.QGridLayout(btn_group)
        btn_group_layout.setContentsMargins(10, 20, 10, 10)
        btn_group_layout.setSpacing(10)

        btn_load = QtWidgets.QPushButton("加载 OBJ")
        btn_load.clicked.connect(self.load_obj)
        btn_load.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogOpenButton))
        btn_group_layout.addWidget(btn_load, 0, 0)


        btn_split = QtWidgets.QPushButton("分割模型")
        btn_split.clicked.connect(self.split_model)
        btn_split.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_FileDialogDetailedView))
        btn_group_layout.addWidget(btn_split, 0, 1)

        btn_merge = QtWidgets.QPushButton("合并并保存")
        btn_merge.clicked.connect(self.merge_components)
        btn_merge.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogSaveButton))
        btn_group_layout.addWidget(btn_merge, 1, 0)

        right_panel_layout.addWidget(btn_group)

        # 显示选项组
        display_group = QtWidgets.QGroupBox("显示选项")
        display_group_layout = QtWidgets.QVBoxLayout(display_group)
        display_group_layout.setContentsMargins(10, 20, 10, 10)
        display_group_layout.setSpacing(10)

        # 视图控制
        view_layout = QtWidgets.QHBoxLayout()

        btn_reset = QtWidgets.QPushButton("重置视图")
        btn_reset.clicked.connect(self.reset_view)
        view_layout.addWidget(btn_reset)

        btn_bg_color = QtWidgets.QPushButton("背景颜色")
        btn_bg_color.clicked.connect(self.set_background_color)
        view_layout.addWidget(btn_bg_color)

        display_group_layout.addLayout(view_layout)

        # 显示模式
        mode_layout = QtWidgets.QHBoxLayout()

        self.wireframe_action = QtWidgets.QPushButton("线框模式")
        self.wireframe_action.setCheckable(True)
        self.wireframe_action.toggled.connect(self.toggle_wireframe)
        mode_layout.addWidget(self.wireframe_action)

        self.surface_action = QtWidgets.QPushButton("表面模式")
        self.surface_action.setCheckable(True)
        self.surface_action.setChecked(True)
        self.surface_action.toggled.connect(self.toggle_surface)
        mode_layout.addWidget(self.surface_action)

        display_group_layout.addLayout(mode_layout)

        # 透明度控制
        opacity_label = QtWidgets.QLabel("透明度:")
        display_group_layout.addWidget(opacity_label)

        self.opacity_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self.opacity_slider.setRange(0, 100)
        self.opacity_slider.setValue(100)
        self.opacity_slider.valueChanged.connect(self.update_opacity)
        display_group_layout.addWidget(self.opacity_slider)

        right_panel_layout.addWidget(display_group)

        # 组件列表
        component_group = QtWidgets.QGroupBox("组件列表")
        component_group_layout = QtWidgets.QVBoxLayout(component_group)
        component_group_layout.setContentsMargins(0, 20, 0, 0)

        # 搜索框
        self.search_box = QtWidgets.QLineEdit()
        self.search_box.setPlaceholderText("搜索组件...")
        self.search_box.textChanged.connect(self.filter_components)
        component_group_layout.addWidget(self.search_box)

        # 列表控件
        self.list_widget = QtWidgets.QListWidget()
        self.list_widget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
        self.list_widget.itemChanged.connect(self.update_display)
        component_group_layout.addWidget(self.list_widget)

        # 列表操作按钮
        list_btn_layout = QtWidgets.QHBoxLayout()

        btn_select_all = QtWidgets.QPushButton("全选")
        btn_select_all.clicked.connect(self.select_all_components)
        list_btn_layout.addWidget(btn_select_all)

        btn_deselect_all = QtWidgets.QPushButton("全不选")
        btn_deselect_all.clicked.connect(self.deselect_all_components)
        list_btn_layout.addWidget(btn_deselect_all)

        component_group_layout.addLayout(list_btn_layout)

        right_panel_layout.addWidget(component_group)
        right_panel_layout.addStretch(1)

        # 底部按钮
        btn_box = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
        )
        btn_box.accepted.connect(self.accept)
        btn_box.rejected.connect(self.reject)
        right_panel_layout.addWidget(btn_box)

        main_layout.addWidget(right_panel, 1)

        # 如果有传入的模型数据，立即显示
        if self.obj_data:
            self.plot_original()

    def set_background_color(self):
        color = QtWidgets.QColorDialog.getColor(initial=QColor(self.background_color),
                                                parent=self,
                                                title="选择背景颜色")
        if color.isValid():
            self.background_color = color.name()
            self.view.setBackgroundColor(self.background_color)

    def reset_view(self):
        if self.obj_data:
            self.auto_zoom()

    def update_opacity(self, value):
        opacity = value / 100.0
        try:
            if self.current_mesh:
                color = self.color_map[0] if hasattr(self, 'color_map') else (0.8, 0.8, 0.8, opacity)
                self.current_mesh.setColor((color[0], color[1], color[2], opacity))

            for i, mesh in enumerate(self.component_meshes):
                if i < len(self.color_map):
                    color = self.color_map[i]
                    mesh.setColor((color[0], color[1], color[2], opacity))
                else:
                    mesh.setColor((0.8, 0.8, 0.8, opacity))
        except Exception as e:
            print(f"更新透明度时出错: {str(e)}")

    def toggle_wireframe(self, checked):
        self.wireframe_mode = checked
        self.update_display_mode()

    def toggle_surface(self, checked):
        self.surface_mode = checked
        self.update_display_mode()

    def update_display_mode(self):
        if not self.current_mesh:
            return

        if self.wireframe_mode:
            self.current_mesh.setGLOptions('additive')
            for mesh in self.component_meshes:
                mesh.setGLOptions('additive')
        else:
            self.current_mesh.setGLOptions('opaque')
            for mesh in self.component_meshes:
                mesh.setGLOptions('opaque')

        if self.surface_mode:
            self.current_mesh.meshDataChanged()
            for mesh in self.component_meshes:
                mesh.meshDataChanged()

    def select_all_components(self):
        for i in range(self.list_widget.count()):
            item = self.list_widget.item(i)
            item.setCheckState(QtCore.Qt.Checked)

    def deselect_all_components(self):
        for i in range(self.list_widget.count()):
            item = self.list_widget.item(i)
            item.setCheckState(QtCore.Qt.Unchecked)

    def filter_components(self):
        search_text = self.search_box.text().lower()
        for i in range(self.list_widget.count()):
            item = self.list_widget.item(i)
            item_text = item.text().lower()
            item.setHidden(search_text not in item_text)

    def load_obj(self):
        path, _ = QtWidgets.QFileDialog.getOpenFileName(
            self, "打开 OBJ 文件", "", "OBJ 文件 (*.obj)")
        if not path:
            return

        try:
            self.obj_data = self.parse_obj(path)
            self.plot_original()
        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"加载文件失败:\n{str(e)}")

    def parse_obj(self, path):
        vertices = []
        faces = []

        with open(path, 'r') as f:
            for line in f:
                if line.startswith('v '):
                    parts = line.strip().split()
                    vertices.append([float(p) for p in parts[1:]])
                elif line.startswith('f '):
                    parts = line.strip().split()
                    face = []
                    for part in parts[1:]:
                        v_idx = int(part.split('/')[0]) - 1
                        face.append(v_idx)
                    faces.append(face)

        if not vertices or not faces:
            raise ValueError("无效的 OBJ 文件 - 缺少顶点或面数据")

        return {
            'vertices': np.array(vertices),
            'faces': faces,
            'path': path
        }

    def plot_original(self):
        try:
            self.view.clear()
            self.text_items = []
            self.line_items = []
            self.component_meshes = []

            if self.obj_data is None or len(self.obj_data['vertices']) == 0:
                return

            # 确保所有面都是有效的三角形
            valid_faces = []
            for face in self.obj_data['faces']:
                if len(set(face)) == 3:  # 确保是三角形且没有重复顶点
                    v1, v2, v3 = face
                    # 检查是否退化（顶点共线）
                    a = self.obj_data['vertices'][v1]
                    b = self.obj_data['vertices'][v2]
                    c = self.obj_data['vertices'][v3]

                    # 计算三角形面积
                    ab = b - a
                    ac = c - a
                    cross = np.cross(ab, ac)
                    area = 0.5 * np.linalg.norm(cross)

                    if area > 1e-6:  # 忽略面积过小的三角形
                        valid_faces.append(face)

            # 使用有效面创建网格
            self.original_mesh = self.create_mesh(
                self.obj_data['vertices'],
                valid_faces,
                (0.8, 0.8, 0.8, 1)
            )
            self.current_mesh = self.original_mesh
            self.view.addItem(self.original_mesh)

            # 自动调整视图
            self.auto_zoom()
        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"绘制模型时出错:\n{str(e)}")

    def auto_zoom(self):
        if not self.obj_data:
            return

        vertices = self.obj_data['vertices']
        if len(vertices) == 0:
            return

        # 计算边界框
        min_coords = np.min(vertices, axis=0)
        max_coords = np.max(vertices, axis=0)
        size = np.max(max_coords - min_coords)

        # 计算中心点和距离
        center = (min_coords + max_coords) / 2
        distance = size * 2

        # 使用 QVector3D
        center_vec = QVector3D(float(center[0]), float(center[1]), float(center[2]))
        camera_pos = QVector3D(float(center[0]), float(center[1]), float(center[2] + distance))

        self.view.setCameraPosition(
            pos=camera_pos,
            distance=distance,
            elevation=30,
            azimuth=45
        )

    def split_model(self):
        if self.obj_data is None:
            QtWidgets.QMessageBox.warning(self, "警告", "请先加载 OBJ 文件")
            return

        try:
            uf = UnionFind()

            # 只处理有效的面（非退化三角形）
            valid_faces = []
            for face in self.obj_data['faces']:
                if len(set(face)) == 3:  # 确保是三角形且没有重复顶点
                    v1, v2, v3 = face
                    # 检查是否退化（顶点共线）
                    a = self.obj_data['vertices'][v1]
                    b = self.obj_data['vertices'][v2]
                    c = self.obj_data['vertices'][v3]

                    # 计算三角形面积
                    ab = b - a
                    ac = c - a
                    cross = np.cross(ab, ac)
                    area = 0.5 * np.linalg.norm(cross)

                    if area > 1e-6:  # 忽略面积过小的三角形
                        valid_faces.append(face)
                        # 将三角形的顶点连接起来
                        uf.union(v1, v2)
                        uf.union(v2, v3)
                        uf.union(v3, v1)

            groups = defaultdict(list)
            for v_idx in range(len(self.obj_data['vertices'])):
                root = uf.find(v_idx)
                groups[root].append(v_idx)

            self.groups = []
            all_vertices = self.obj_data['vertices']
            for idx, (root, v_indices) in enumerate(groups.items()):
                component_vertices = all_vertices[v_indices]
                center = np.mean(component_vertices, axis=0)

                index_map = {v: i for i, v in enumerate(v_indices)}
                group_faces = []
                for face in valid_faces:
                    if all(v in v_indices for v in face):
                        converted_face = [index_map[v] for v in face]
                        group_faces.append(converted_face)

                self.groups.append({
                    'vertices': component_vertices,
                    'faces': group_faces,
                    'v_indices': v_indices,
                    'color': self.color_map[idx % len(self.color_map)],
                    'center': center,
                    'number': idx + 1
                })

            self.list_widget.clear()
            for i in range(len(self.groups)):
                item = QtWidgets.QListWidgetItem(f"组件 {i + 1} (顶点: {len(self.groups[i]['vertices'])})")
                item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
                item.setCheckState(QtCore.Qt.Checked)
                self.list_widget.addItem(item)

            self.update_display()
        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"分割模型时出错:\n{str(e)}")

    def create_mesh(self, vertices, faces, color):
        mesh = gl.GLMeshItem(
            vertexes=vertices,
            faces=np.array(faces),
            color=color,
            smooth=True,
            shader='shaded',
            glOptions='opaque'
        )

        if self.wireframe_mode:
            mesh.setGLOptions('additive')

        return mesh

    def update_display(self):
        self.view.clear()
        self.text_items = []
        self.line_items = []
        self.component_meshes = []

        # 添加坐标轴
        axis = gl.GLAxisItem()
        axis.setSize(10, 10, 10)
        self.view.addItem(axis)

        selected_count = 0
        for i in range(self.list_widget.count()):
            if self.list_widget.item(i).checkState() == QtCore.Qt.Checked:
                group = self.groups[i]
                mesh = self.create_mesh(group['vertices'], group['faces'], group['color'])
                self.view.addItem(mesh)
                self.component_meshes.append(mesh)
                selected_count += 1

    def merge_components(self):
        selected = [i for i in range(self.list_widget.count())
                    if self.list_widget.item(i).checkState() == QtCore.Qt.Checked]

        if len(selected) < 1:
            QtWidgets.QMessageBox.warning(self, "警告", "请先选择要合并的组件")
            return

        # 获取保存路径和文件名
        default_path = ""
        if self.obj_data and 'path' in self.obj_data:
            original_path = self.obj_data['path']
            dirname = os.path.dirname(original_path)
            basename = os.path.splitext(os.path.basename(original_path))[0]
            default_path = os.path.join(dirname, f"{basename}_merged.obj")

        path, _ = QtWidgets.QFileDialog.getSaveFileName(
            self, "保存合并文件", default_path, "OBJ 文件 (*.obj)")
        if not path:
            return

        # 自动添加文件扩展名
        if not path.lower().endswith('.obj'):
            path += '.obj'

        try:
            # 合并顶点和面
            merged_vertices = []
            merged_faces = []
            vertex_map = {}  # 全局索引到合并后索引的映射

            for idx in selected:
                group = self.groups[idx]
                for old_idx in group['v_indices']:
                    if old_idx not in vertex_map:
                        vertex_map[old_idx] = len(merged_vertices)
                        merged_vertices.append(self.obj_data['vertices'][old_idx])

                for face in group['faces']:
                    merged_face = [vertex_map[group['v_indices'][v]] + 1 for v in face]
                    merged_faces.append(merged_face)

            # 保存合并后的模型
            with open(path, 'w') as f:
                # 添加注释
                f.write("# 由高级 OBJ 组件分割管理器生成\n")
                f.write(f"# 原始文件: {self.obj_data.get('path', '未知')}\n")
                f.write(f"# 合并了 {len(selected)} 个组件\n\n")

                # 写入顶点
                for v in merged_vertices:
                    f.write(f"v {v[0]:.6f} {v[1]:.6f} {v[2]:.6f}\n")

                # 写入面
                f.write("\n")
                for face in merged_faces:
                    f.write(f"f {' '.join(map(str, face))}\n")

            QtWidgets.QMessageBox.information(self, "保存成功", f"合并文件已保存到：\n{path}")
        except Exception as e:
            QtWidgets.QMessageBox.critical(self, "错误", f"保存文件时出错:\n{str(e)}")